package cn.turboinfo.dongying.api.provider.admin.framework.shiro.realm;

import cn.turboinfo.dongying.api.domain.admin.service.permission.PermissionService;
import cn.turboinfo.dongying.api.domain.common.service.role.RolePermissionService;
import cn.turboinfo.dongying.api.domain.common.service.role.RoleService;
import cn.turboinfo.dongying.api.domain.common.service.user.SysUserRoleService;
import cn.turboinfo.dongying.api.domain.common.service.user.SysUserService;
import cn.turboinfo.dongying.api.domain.common.service.user.UserCredentialService;
import cn.turboinfo.dongying.api.domain.common.service.user.UserLoginService;
import cn.turboinfo.dongying.api.entity.admin.pojo.permission.Permission;
import cn.turboinfo.dongying.api.entity.admin.pojo.role.Role;
import cn.turboinfo.dongying.api.entity.admin.pojo.role.RolePermission;
import cn.turboinfo.dongying.api.entity.admin.pojo.user.SysUserRole;
import cn.turboinfo.dongying.api.entity.common.enumeration.user.LoginCheckType;
import cn.turboinfo.dongying.api.entity.common.enumeration.user.LoginNameType;
import cn.turboinfo.dongying.api.entity.common.exception.user.SysUserException;
import cn.turboinfo.dongying.api.entity.common.pojo.user.SysUser;
import cn.turboinfo.dongying.api.entity.common.pojo.user.UserCredential;
import cn.turboinfo.dongying.api.entity.common.pojo.user.UserLogin;
import cn.turboinfo.dongying.api.provider.admin.component.session.AdminSessionHelper;
import cn.turboinfo.dongying.api.provider.admin.framework.shiro.session.AdminSession;
import cn.turboinfo.dongying.api.provider.admin.component.session.AdminSessionHelper;
import cn.turboinfo.dongying.api.provider.admin.framework.shiro.session.AdminSession;
import lombok.extern.slf4j.Slf4j;
import net.sunshow.toolkit.core.base.enums.EnableStatus;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class SysUserRealm extends AuthorizingRealm {

    // 保留的 admin 角色, 以保证资源和角色管理功能不会被误取消掉
    final public static String INTERNAL_ADMIN_ROLE = "__admin__";

    private SysUserService sysUserService;

    private PermissionService permissionService;

    private SysUserRoleService sysUserRoleService;

    private RolePermissionService rolePermissionService;

    private RoleService roleService;

    private AdminSessionHelper adminSessionHelper;

    private UserCredentialService userCredentialService;

    private UserLoginService userLoginService;

    public SysUserRealm() {
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        Set<String> resourceSet = new HashSet<>();

        AdminSession session = adminSessionHelper.getSession();

        List<SysUserRole> sysUserRoleList = sysUserRoleService.findBySysUserId(session.getSysUserId());

        Set<Long> roleIdSet = sysUserRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());

        List<Role> roleList = roleService.findByIdCollection(roleIdSet);
        roleList.forEach(role -> authorizationInfo.addRole(role.getCode()));

        // 所有角色的权限列表
        List<RolePermission> rolePermissionList = rolePermissionService.findByRoleIdCollection(roleIdSet);

        List<Permission> permissionList = permissionService.findByIdCollection(rolePermissionList.stream().map(RolePermission::getPermissionId).collect(Collectors.toSet()));
        resourceSet.addAll(permissionList.stream().map(Permission::getResource).collect(Collectors.toSet()));

        authorizationInfo.setStringPermissions(resourceSet);

        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        String username = token.getUsername();

        SysUser sysUser = sysUserService.getByUsername(username).orElseThrow(() -> new SysUserException("用户不存在"));

        if (sysUser.getStatus() != EnableStatus.ENABLED) {
            log.error("用户未启用或已被禁用, userId={}, userName={}", sysUser.getId(), sysUser.getUsername());
            throw new SysUserException("用户未启用");
        }

        // 获取 user login
        List<UserLogin> userLoginList = userLoginService
                .findByLoginNameAndLoginNameTypeAndLoginCheckType(username, LoginNameType.USERNAME, LoginCheckType.ADMIN_CREDENTIAL);
        if (userLoginList.isEmpty()) {
            throw new RuntimeException("不支持的登录方式");
        }
        UserLogin userLogin = userLoginList.get(0);

        // 获取 user credential
        UserCredential userCredential = userCredentialService.getByIdEnsure(userLogin.getLoginCheckId());

        return new SimpleAuthenticationInfo(sysUser.getId(), userCredential.getCredential(), ByteSource.Util.bytes(userCredential.getSalt()), getName());
    }

    @Autowired
    public void setSysUserService(SysUserService sysUserService) {
        this.sysUserService = sysUserService;
    }

    @Autowired
    public void setPermissionService(PermissionService permissionService) {
        this.permissionService = permissionService;
    }

    @Autowired
    public void setSysUserRoleService(SysUserRoleService sysUserRoleService) {
        this.sysUserRoleService = sysUserRoleService;
    }

    @Autowired
    public void setRolePermissionService(RolePermissionService rolePermissionService) {
        this.rolePermissionService = rolePermissionService;
    }

    @Autowired
    public void setRoleService(RoleService roleService) {
        this.roleService = roleService;
    }

    @Autowired
    public void setAdminSessionHelper(AdminSessionHelper adminSessionHelper) {
        this.adminSessionHelper = adminSessionHelper;
    }

    @Autowired
    public void setUserCredentialService(UserCredentialService userCredentialService) {
        this.userCredentialService = userCredentialService;
    }

    @Autowired
    public void setUserLoginService(UserLoginService userLoginService) {
        this.userLoginService = userLoginService;
    }
}
